﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Audio;

namespace TheVoid.Model
{
    public class TextManager : DrawableGameComponent
    {
        enum FadeState
        {
            None = 0,
            FadingIn = 1,
            FadingOut = 2
        }

        public const int LINE_HEIGHT = 25;
        public const int LINE_OFFSET = 7;
        public const float CHARACTER_SCROLL_TIME = 0.015f;
        public const float FADE_TIME = 2.5f;

        public int X = 0;
        public int Y = 0;
        public int Width = 0;
        public int Height = 0;
        public bool Active = true;

        
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        SpriteFont defaultFont;
        Color backgroundColor = Color.Black;
        List<Output> texts;
        SoundEffectInstance keySound;

        int newDrawCharCount = 0;
        float characterScrollTimer = CHARACTER_SCROLL_TIME;
        bool scrolling = false;

        float fadeTimer = 0;
        float alpha = 1;
        FadeState fadeState = FadeState.None;

        Random ran = new Random();

        public TextManager(Game _game, GraphicsDeviceManager graphics)
            : base(_game)
        {
            this.graphics = graphics;
            texts = new List<Output>();
        }

        protected override void LoadContent()
        {
            base.LoadContent();
            spriteBatch = new SpriteBatch(GraphicsDevice);
            defaultFont = Game.Content.Load<SpriteFont>("DefaultFont");
            keySound = Game.Content.Load<SoundEffect>("beep").CreateInstance();
            keySound.Volume = 0.2f;
        }

        public override void Draw(GameTime gameTime)
        {
            switch (fadeState)
            {
                case FadeState.None:        fadeTimer = 0; break;
                case FadeState.FadingIn:    fadeTimer -= (float)gameTime.ElapsedGameTime.TotalSeconds; 
                                            alpha = 1 - fadeTimer/FADE_TIME;
                                            if (fadeTimer < 0)
                                                fadeState = FadeState.None;
                                            break;
                case FadeState.FadingOut:   fadeTimer -= (float)gameTime.ElapsedGameTime.TotalSeconds; 
                                            alpha = fadeTimer/FADE_TIME;
                                            if (fadeTimer < 0)
                                                fadeState = FadeState.None;
                                            break;
            }


            if (Active)
            {
                if (scrolling)
                {
                    characterScrollTimer -= (float)gameTime.ElapsedGameTime.TotalSeconds;
                    if (characterScrollTimer < 0)
                    {
                        if (newDrawCharCount < texts[texts.Count - 1].Text.Length)
                        {
                            newDrawCharCount++;
                            if (newDrawCharCount % 1 == 0)
                            {
                                keySound.Play();
                                keySound.Pitch = ((float)ran.NextDouble() - 0.5f) - 0.5f;
                                keySound.Pan = ((float)ran.NextDouble() - 0.5f) * 2;
                            }
                        }
                        else
                            scrolling = false;
                        characterScrollTimer = CHARACTER_SCROLL_TIME;
                    }
                }

                int lastYPos = Y;
                GraphicsDevice.Clear(backgroundColor);

                if (texts.Count > 0)
                {
                    spriteBatch.Begin();

                    lastYPos -= DrawOutput(texts[texts.Count - 1], newDrawCharCount, lastYPos) * LINE_HEIGHT + LINE_OFFSET; 
                    for (int i = texts.Count - 2; i >= 0; i--)
                    {
                        lastYPos -= DrawOutput(texts[i], texts[i].Text.Length, lastYPos) * LINE_HEIGHT + LINE_OFFSET;
                        if (lastYPos < Y - Height)
                            break;
                    }
                    spriteBatch.End();
                }
            }
        }

        public void FadeOut()
        {
            fadeState = FadeState.FadingOut;
            fadeTimer = FADE_TIME;
        }

        public void SkipScrolling()
        {
            scrolling = false;
            newDrawCharCount = texts[texts.Count - 1].Text.Length;
        }

        public void FadeIn()
        {
            fadeState = FadeState.FadingIn;
            fadeTimer = FADE_TIME;
        }

        public void print(String text, Person person, bool _scroll)
        {
            if (_scroll)
            {
                scrolling = true;
                characterScrollTimer = CHARACTER_SCROLL_TIME;
                newDrawCharCount = 0;
            }
            else
            {
                scrolling = false;
                newDrawCharCount = text.Length;
            }

            foreach(string id in Game1.Instance.mPersonManager.IDs)
                text = text.Replace(id, Game1.Instance.mPersonManager.GetPerson(id).GetName(Game1.Iteration));

            Output o = new Output {Pers=person, Text = text };
            this.texts.Add(o);
        }

        public void Clear()
        {
            texts.Clear();
        }

        private int DrawOutput(Output _out, int _charCount, int _yPos)
        {
            if (_charCount == 0)
                return 1;

            int writtenLineCount = 0;
            string textLeft = _out.Text.Substring(0, _charCount);
            Vector2 textSize = defaultFont.MeasureString(textLeft);

            /*while (textSize.X > Width + X)
            {
                float tooLong = textSize.X % (Width + X) + 1;
                float percent = tooLong / textSize.X;

                if (percent == 0)
                    percent = 0;

                int charCount = (int)Math.Floor(percent * textLeft.Length);

                string alt = textLeft.Substring(textLeft.Length - charCount - 1);
                textLeft = textLeft.Remove(textLeft.Length - charCount - 1);
                if (textLeft[textLeft.Length - 1] == ' ')
                    textLeft.Remove(textLeft.Length - 1);

                if (alt[0] == ' ')
                   alt = alt.Remove(0, 1);
                else
                {
                    int lastSpaceIndex = textLeft.LastIndexOf(" ");
                    alt = textLeft.Substring(lastSpaceIndex + 1) + alt;
                    string tempTextLeft = textLeft;
                    textLeft = textLeft.Remove(lastSpaceIndex);
                }


                spriteBatch.DrawString(defaultFont, alt, new Vector2(X, _yPos - LINE_HEIGHT * writtenLineCount), SetAlpha(_out.Pers.Color, alpha));
                writtenLineCount++;
                textSize = defaultFont.MeasureString(textLeft);

                if (_yPos - LINE_HEIGHT * writtenLineCount < Y - Height)
                    break;
            }*/

            if (_yPos - LINE_HEIGHT * writtenLineCount > Y - Height)
            {
                spriteBatch.DrawString(defaultFont, textLeft, new Vector2(X, _yPos - LINE_HEIGHT * writtenLineCount - textSize.Y), SetAlpha(_out.Pers.Color, alpha));
                writtenLineCount += (int)(textSize.Y / 30) + 1;
            }

            return writtenLineCount;
        }

        private Color SetAlpha(Color _col, float _alpha)
        {
            return Color.FromNonPremultiplied(_col.R, _col.G, _col.B, (int)(255 * _alpha));
        }

        private class Output
        {
            public String Text { get; set; }
            public Person Pers { get; set; }
        }
    }
}
